package com.wuyb.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wuyb.enums.ErrorCode;
import com.wuyb.mapper.*;
import com.wuyb.pojo.*;
import com.wuyb.pojo.config.ImgPathPro;
import com.wuyb.pojo.entity.BaseResponse;
import com.wuyb.pojo.entity.ResultUtils;
import com.wuyb.pojo.param.*;
import com.wuyb.service.ChatFilterService;
import com.wuyb.service.RoleService;
import com.wuyb.service.UserService;
import com.wuyb.socket.SocketSender;
import com.wuyb.util.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.nio.file.Paths;
import java.security.Principal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 用户服务接口实现类
 *
 * @Author: wyb
 * @Date: 2024/01/03/14:52
 * @Description:
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleService roleService;

    @Resource
    private ImgPathPro imgPathPro;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private MeetingUserMapper meetingUserMapper;

    @Resource
    private MeetingMapper meetingMapper;

    @Resource
    private UserFriendInformMapper userFriendInformMapper;

    @Resource
    private UserFriendMapper userFriendMapper;

    @Resource
    private SocketSender socketSender;

    @Resource
    private ChatFilterService chatFilterService;

    @Resource
    private FileUtil fileUtil;

    @Resource
    private RedisUtil redisUtil;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    /**
     * 登录
     *
     * @param users
     * @param request
     * @param jwtTokenUtil
     * @param userDetailsService
     * @param passwordEncoder
     * @return
     */
    @Override
    public BaseResponse<?> login(UserLoginParam users, HttpServletRequest request, JwtTokenUtil jwtTokenUtil, UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        if (!validCaptcha(request, users.getCode())) {
            return ResultUtils.error(ErrorCode.OPERATION_ERROR.getCode(), "验证码输入错误");
        }
        UserDetails userDetails = userDetailsService.loadUserByUsername(users.getUsername());
        if (userDetails == null || !passwordEncoder.matches(users.getPassword(), userDetails.getPassword())) {
            return ResultUtils.error(ErrorCode.OPERATION_ERROR.getCode(), "用户名输入错误");
        }
        Integer userId = getUserIdByUserName(userDetails.getUsername());
        if (redisUtil.getUserOnline(userId) != null) {
            return ResultUtils.error(ErrorCode.OPERATION_ERROR.getCode(), "该用户已在线");
        }

        return saveLogin(jwtTokenUtil, userDetails, userId);
    }

    /**
     * 注册
     *
     * @param users
     * @param request
     * @param passwordEncoder
     * @return
     */
    @Override
    public BaseResponse<?> register(UserRegisterParam users, HttpServletRequest request, PasswordEncoder passwordEncoder) {
        if (!validCaptcha(request, users.getCode())) {
            return ResultUtils.error(ErrorCode.OPERATION_ERROR.getCode(), "验证码输入错误");
        }
        User user = new User()
                .setUsername(users.getUsername())
                .setPassword(users.getPassword())
                .setTelephone(users.getTelephone())
                .setEmail(users.getEmail())
                .setName(users.getName());

        try {
            userAdd(user, passwordEncoder);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultUtils.error(ErrorCode.OPERATION_ERROR.getCode(), e.getMessage());
        }
        return ResultUtils.success("注册成功");
    }

    /**
     * 获取管理员信息
     *
     * @param principal
     * @param servletRequest
     * @return
     */
    @Override
    public BaseResponse<?> getAdminInfo(Principal principal, HttpServletRequest servletRequest) {
        if (principal == null) {
            return null;
        }
        User user = getUserByUserName(principal.getName());
        // 防止密码泄漏，设置为空返回
        user.setPassword(null);
        return ResultUtils.success(user);
    }

    /**
     * 分页查询所有用户
     *
     * @param current
     * @param size
     * @return
     */
    @Override
    public BaseResponse<?> select(Integer current, Integer size) {
        IPage<User> list = listPage(new PageParam(current, size));
        return ResultUtils.success(list);
    }

    /**
     * 添加用户
     *
     * @param users
     * @param passwordEncoder
     * @return
     */
    @Override
    public BaseResponse<?> add(UserAddParam users, PasswordEncoder passwordEncoder) {
        try {
            userAdd(UserParamConverter.getUser(users), passwordEncoder);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResultUtils.error(ErrorCode.OPERATION_ERROR.getCode(), e.getMessage());
        }

        return ResultUtils.success(users, "添加成功");
    }

    /**
     * 更新用户
     *
     * @param users
     * @param uid
     * @param passwordEncoder
     * @return
     */
    @Override
    public BaseResponse<?> update(UserUpdateParam users, Integer uid, PasswordEncoder passwordEncoder) {
        users.setId(uid);
        User one = userMapper.selectOne(new QueryWrapper<User>().eq("username_xq", users.getUsername()));
        if (one != null && !one.getId().equals(uid)) {
            return ResultUtils.error(ErrorCode.OPERATION_ERROR.getCode(), "修改失败，用户名重复");
        }
        one = userMapper.selectOne(new QueryWrapper<User>().eq("name_xq", users.getName()));
        if (one != null && !one.getId().equals(uid)) {
            return ResultUtils.error(ErrorCode.OPERATION_ERROR.getCode(), "修改失败，名称重复");
        }
        if (StringUtils.hasText(users.getPassword())) {
            users.setPassword(passwordEncoder.encode(users.getPassword()));
        }
        if (userMapper.updateById(UserParamConverter.getUser(users)) > 0) {
            return ResultUtils.success(users, "修改成功");
        } else {
            return ResultUtils.error("修改失败");
        }
    }

    /**
     * 删除用户信息
     *
     * @param uid
     * @return
     */
    @Override
    public BaseResponse<?> delete(Integer uid) {
        User user = userDelById(uid);
        if (user != null) {
            return ResultUtils.success(user, "删除成功");
        } else {
            return ResultUtils.error("删除失败");
        }
    }

    /**
     * 设置管理员
     *
     * @param param
     * @return
     */
    @Override
    public BaseResponse<?> setAdmin(UserSetAdminParam param) {
        int result = userMapper.updateById(new User().setId(param.getId()).setIsAdmin(param.getIsAdmin()));
        if (result != 0) {
            return ResultUtils.success("修改成功");
        }
        return ResultUtils.error("修改失败");
    }

    /**
     * 设置头像
     *
     * @param param
     * @return
     */
    @Override
    public BaseResponse<?> setHeadImg(UserSetImgParam param) {
        BaseResponse<?> baseResponse = uploadImg(param.getFile(), imgPathPro.getHead());
        if (baseResponse.getCode() == 200) {
            User user = new User().setId(param.getId()).setHeadImage((String) baseResponse.getData());
            userMapper.updateById(user);
            return ResultUtils.success(user, baseResponse.getMessage());
        }
        return baseResponse;
    }

    /**
     * 会议签到功能
     *
     * @param param
     * @param meetingId
     * @return
     */
    @Override
    public BaseResponse<?> signIn(ImgBase64Param param, String meetingId) {
        if (getHadSignIn(meetingId)) {
            return ResultUtils.error("你已经签到过了");
        }
        // 判断签到的时间，签到时间必须再会议开始之后
        LocalDateTime startDate = meetingMapper.selectById(meetingId).getStartDate();
        LocalDateTime now = LocalDateTime.now();
        if (startDate.isAfter(now)) {
            return ResultUtils.error("会议开始之后才能进行签到");
        }
        QueryWrapper<MeetingUser> queryWrapper = new QueryWrapper<MeetingUser>()
                .eq("meeting_id_xq", meetingId)
                .eq("users_id_xq", MyUtil.getUser().getId());
        meetingUserMapper.update(
                (new MeetingUser())
                        .setHadSignIn(true)
                        .setHadSignInTime(now), queryWrapper);

        return ResultUtils.success("签到成功");
    }

    /**
     * 是否已经签到
     *
     * @param meetingId
     * @return
     */
    @Override
    public BaseResponse<?> hadSignIn(String meetingId) {
        return ResultUtils.success(getHadSignIn(meetingId));
    }

    /**
     * 通过用户名字获取用户信息
     *
     * @param userName
     * @return
     */
    @Override
    public BaseResponse<?> getUserInfoByName(String userName) {
        return getUserInfo("name_xq", userName);
    }

    /**
     * 发送好友邀请
     *
     * @param userid
     * @return
     */
    @Override
    public BaseResponse<?> sendFriApplication(Integer userid) {
        if (userMapper.selectById(userid) == null) {
            return ResultUtils.error("申请失败，该用户不存在");
        }
        Integer ownerId = MyUtil.getUser().getId();
        if (ownerId.equals(userid)) {
            return ResultUtils.error("申请失败，不能对自己发送邀请");
        }

        if (userFriendInformMapper.selectCount(new QueryWrapper<UsersFriendInform>()
                .eq("type_xq", 0)
                .eq("from_id_xq", ownerId)
                .eq("to_id_xq", userid)
                .eq("state_xq", 0)
        ) > 0) {
            return ResultUtils.error("申请失败，你已经对该用户发过申请了");
        }

        if (isFriend(userid, ownerId)) {
            return ResultUtils.error("申请失败，你们已经是好友了");
        }

        UsersFriendInform usersFriendInform = UsersFriendInform.builder()
                .type(0)
                .fromId(ownerId)
                .content("")
                .toId(userid)
                .state(0)
                .sendTime(LocalDateTime.now())
                .build();

        userFriendInformMapper.insert(usersFriendInform);
        usersFriendInform.setFromer(MyUtil.getUser());
        JSONObject jsonObject = JSONObject.from(usersFriendInform);
        socketSender.sendChat(jsonObject);
        return ResultUtils.success(jsonObject);
    }

    /**
     * 回应好友申请
     *
     * @param userid
     * @param result 0 忽略 1 同意
     * @return
     */
    @Override
    public BaseResponse<?> replyFriApplication(Integer userid, Integer result) {
        Integer ownerId = MyUtil.getUser().getId();
        UsersFriendInform usersFriendInform = userFriendInformMapper.selectOne(
                (new QueryWrapper<UsersFriendInform>())
                        .eq("type_xq", 0)
                        .eq("from_id_xq", userid)
                        .eq("to_id_xq", ownerId)
                        .eq("state_xq", 0)
        );
        if (usersFriendInform == null) {
            return ResultUtils.error("处理失败，找不到处理对象");
        }
        // 已读通知
        usersFriendInform.setState(1);
        userFriendInformMapper.updateById(usersFriendInform);

        if (result == 1) {
            if (userid > ownerId) {
                swapUserId(userid, ownerId);
            }
            userFriendMapper.insert(
                    new UserFriend()
                            .setUserId1(userid)
                            .setUserId2(ownerId)
            );
            sendFriChat(userid, "我同意了你的好友申请，现在可以进行聊天了");
            return ResultUtils.success("同意添加好友");
        } else {
            return ResultUtils.error("忽略好友申请");
        }
    }

    @Override
    public BaseResponse<?> getFriApplications(Integer current, Integer size) {
        Integer ownerId = MyUtil.getUser().getId();
        Page<UsersFriendInform> page = new Page<>(current, size);
        IPage<UsersFriendInform> usersFriendInformIPage = userFriendInformMapper.selectPageWithFromerInfo(page,
                new QueryWrapper<UsersFriendInform>()
                        .eq("type_xq", 0)
                        .eq("to_id_xq", ownerId)
                        .eq("state_xq", 0)
                        .orderByDesc("send_time_xq")
        );
        return ResultUtils.success(usersFriendInformIPage);
    }

    @Override
    public BaseResponse<?> delFriendById(Integer userid) {
        Integer ownerId = MyUtil.getUser().getId();

        if (userid > ownerId) {
            swapUserId(userid, ownerId);
        }

        userFriendInformMapper.delete(
                new QueryWrapper<UsersFriendInform>()
                        .eq("from_id_xq", userid)
                        .eq("to_id_xq", ownerId)
                        .or()
                        .eq("from_id_xq", ownerId)
                        .eq("to_id_xq", userid)
        );

        int result = userFriendMapper.delete(
                new QueryWrapper<UserFriend>()
                        .eq("user_id1_xq", userid)
                        .eq("user_id2_xq", ownerId)
                        .or()
                        .eq("user_id1_xq", ownerId)
                        .eq("user_id2_xq", userid)
        );
        if (result == 0) {
            return ResultUtils.error("删除失败");
        } else {
            return ResultUtils.success("删除成功");
        }
    }

    @Override
    public BaseResponse<?> getFriendsAndChat(Integer current, Integer size) {
        Integer ownerId = MyUtil.getUser().getId();
        Page<User> page = new Page<>(current, size);
        IPage<User> userIPage = userMapper.selectFriendsAndChatPage(page, ownerId);
        return ResultUtils.success(userIPage);
    }


    @Override
    public BaseResponse<?> getFriChat(Integer userid, Integer current, Integer size) {
        Integer ownerId = MyUtil.getUser().getId();
        Page<UsersFriendInform> page = new Page<>(current, size);
        IPage<UsersFriendInform> usersFriendInformIPage = userFriendInformMapper.selectPage(page,
                new QueryWrapper<UsersFriendInform>()
                        .eq("type_xq", 1)
                        .and(
                                qw -> qw.eq("from_id_xq", ownerId)
                                        .eq("to_id_xq", userid)
                                        .or()
                                        .eq("to_id_xq", ownerId)
                                        .eq("from_id_xq", userid)
                        )
                        .orderByAsc("send_time_xq")
        );
        return ResultUtils.success(usersFriendInformIPage);
    }

    @Override
    public BaseResponse<?> sendFriChat(Integer userid, String conetent) {
        Integer ownerId = MyUtil.getUser().getId();
        if (!isFriend(userid, ownerId)) {
            return ResultUtils.error("发送失败，该好友不存在");
        }
        conetent = chatFilterService.filter(conetent);

        LocalDateTime now = LocalDateTime.now();
        UsersFriendInform usersFriendInform = UsersFriendInform.builder()
                .type(1)
                .fromId(ownerId)
                .toId(userid)
                .content(conetent)
                .state(0)
                .sendTime(now)
                .build();
        userFriendInformMapper.insert(usersFriendInform);
        JSONObject jsonObject = JSONObject.from(usersFriendInform);
        socketSender.sendChat(jsonObject);
        return ResultUtils.success(usersFriendInform);
    }

    @Override
    public BaseResponse<?> getUserInfoById(Integer uid) {
        return getUserInfo("id_xq",uid.toString());
    }

    @Override
    public BaseResponse<?> readFriChat(Integer userid) {
        userFriendInformMapper.update(new UsersFriendInform().setState(1),
                new QueryWrapper<UsersFriendInform>()
                        .eq("from_id_xq", userid)
                        .eq("type_xq", 1)
                        .eq("state_xq", 0)
        );
        return ResultUtils.success("");
    }

    @Override
    public BaseResponse<?> updateOwnerInfo(UserUpdateWithOwnerParam users) {
        Integer ownerId = MyUtil.getUser().getId();
        if (!users.getId().equals(ownerId)) {
            return ResultUtils.error("修改失败，无权限修改他人信息");
        }
        User sameUser = userMapper.selectOne(new QueryWrapper<User>().eq("name_xq", users.getName()));
        if (sameUser != null && !sameUser.getId().equals(ownerId)) {
            return ResultUtils.error("修改失败，名称重复");
        }
        User user = new User()
                .setId(users.getId())
                .setName(users.getName())
                .setEmail(users.getEmail())
                .setTelephone(users.getTelephone());

        int result = userMapper.updateById(user);
        if (result > 0) {
            return ResultUtils.success(user, "修改成功");
        }
        return ResultUtils.error("修改失败");
    }

    @Override
    public BaseResponse<?> updateOwnerPass(UserUpdatePasswordParam users, PasswordEncoder passwordEncoder, HttpServletRequest request) {
        if (!validCaptcha(request, users.getCode())) {
            return ResultUtils.error("验证码输入错误");
        }
        if (!users.getId().equals(MyUtil.getUser().getId())) {
            return ResultUtils.error("修改失败，你不能修改别人的密码");
        }

        User user = userMapper.selectById(users.getId());
        if (passwordEncoder.matches(users.getOldpass(), user.getPassword())) {
            if (passwordEncoder.matches(users.getNewpass(), user.getPassword())) {
                return ResultUtils.error("修改失败，新密码与当前密码重复");
            }
            userMapper.updateById(new User().setId(users.getId()).setPassword(passwordEncoder.encode(users.getNewpass())));
            return ResultUtils.success("修改成功");
        }
        return ResultUtils.error("修改失败，旧密码验证失败");
    }

    /**
     * 根据username获取user
     *
     * @param username
     * @return
     */
    @Override
    public User getUserByUserName(String username) {
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("username_xq", username));
        if (user == null) {
            return null;
        }
        // 查询该用户的角色信息
        List<Role> roles = roleService.selectRoleWithUserId(user.getId());
        List<String> perms = roles.stream().map(Role::getPerms).collect(Collectors.toList());
        if (user.getIsAdmin()) {
            perms.add("admin");
        }
        user.setPerms(perms);
        return user;
    }

    @Override
    public BaseResponse<?> logout() {
        redisUtil.deleteUserOnline(MyUtil.getUser().getId());
        return ResultUtils.success("退出成功");
    }


    /**
     * 比较验证码（验证码保存到Session当中的）
     *
     * @param request
     * @param receiveCaptcha
     * @return
     */
    private boolean validCaptcha(HttpServletRequest request, String receiveCaptcha) {
        String captcha = (String) request.getSession().getAttribute("captcha");
        return captcha != null && captcha.equals(receiveCaptcha);
    }


    /**
     * 根据username获取userId
     *
     * @param username
     * @return
     */
    private Integer getUserIdByUserName(String username) {
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("username_xq", username));
        return user.getId();
    }


    /**
     * 通过登录验证之后，保存登录信息
     *
     * @param jwtTokenUtil
     * @param userDetails
     * @param userId
     * @return
     */
    private BaseResponse<?> saveLogin(JwtTokenUtil jwtTokenUtil, UserDetails userDetails, Integer userId) {
        // 更新Security登录用户的信息
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // 将用户信息放置到安全上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        // 生成Token
        String token = jwtTokenUtil.generateToken(userDetails);
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token", token);
        tokenMap.put("tokenHead", tokenHead);

        // 写入到在线用户当中
        redisUtil.putUserOnline(userId, token);
        return ResultUtils.success(tokenMap, "登录成功");
    }


    /**
     * 增加新的用户，并对密码进行加密
     *
     * @param user
     * @param passwordEncoder
     * @return
     * @throws Exception
     */
    private int userAdd(User user, PasswordEncoder passwordEncoder) throws Exception {
        if (userMapper.selectOne(new QueryWrapper<User>().eq("username_xq", user.getUsername())) != null) {
            throw new Exception("用户名重复");
        }
        if (userMapper.selectOne(new QueryWrapper<User>().eq("name_xq", user.getName())) != null) {
            throw new Exception("名称重复");
        }
        String s = user.getPassword();
        // 对密码进行加密
        String encode = passwordEncoder.encode(user.getPassword());
        user.setPassword(encode);
        user.setHeadImage("img\\head\\default.jpg");
        return userMapper.insert(user);
    }


    /**
     * 分页获取用户列表
     *
     * @param pageParam
     * @return
     */
    private IPage<User> listPage(PageParam pageParam) {
        Page<User> userPage = new Page<>(pageParam.getCurrent(), pageParam.getSize());

        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .select(User.class, e -> !"password_xq".equals(e.getColumn()))
                .ne("username_xq", "root");
        userMapper.selectPage(userPage, wrapper);
        return userPage;

    }

    /**
     * 根据ID删除用户
     *
     * @param userId
     * @return
     */
    private User userDelById(Integer userId) {
        User user = userMapper.selectById(userId);
        if (user != null && userMapper.deleteById(user) > 0) {
            user.setPassword(null);
            return user;
        }
        return null;
    }


    /**
     * 上传头像
     *
     * @param file
     * @param subPath
     * @return
     */
    private BaseResponse<?> uploadImg(MultipartFile file, String subPath) {
        try {
            String filename = fileUtil.uploadImg(file, subPath);
            if (filename != null) {
                return ResultUtils.success(Paths.get(subPath).resolve(filename).toString(), "上传成功");
            }
        } catch (Exception e) {
            return ResultUtils.error("上传失败".concat(e.getMessage()));
        }
        return ResultUtils.error("上传失败");
    }


    /**
     * 检测是否已经签到过了
     *
     * @param meetingId
     * @return
     */
    private Boolean getHadSignIn(String meetingId) {
        QueryWrapper<MeetingUser> queryWrapper = new QueryWrapper<MeetingUser>()
                .eq("meeting_id_xq", meetingId)
                .eq("users_id_xq", MyUtil.getUser().getId());
        MeetingUser meetingUser = meetingUserMapper.selectOne(queryWrapper);
        if (meetingUser == null) {
            return false;
        }
        return true;
    }


    /**
     * 获取用户信息
     *
     * @param key
     * @param value
     * @return
     */
    private BaseResponse<?> getUserInfo(String key, String value) {
        User user = userMapper.selectOne((new QueryWrapper<User>())
                .eq(key, value));
        if (user == null) {
            return ResultUtils.error("没有找到该用户信息");
        }
        user.setPassword(null).setIsAdmin(null);
        return ResultUtils.success(user);
    }


    /**
     * 判断是否已经是好友了
     *
     * @param userId
     * @param userId2
     * @return
     */
    private boolean isFriend(Integer userId, Integer userId2) {
        if (userId > userId2) {
            swapUserId(userId, userId2);
        }

        return userFriendMapper.selectCount(
                (new QueryWrapper<UserFriend>())
                        .eq("user_id1_xq", userId)
                        .eq("user_id2_xq", userId2)
                        .or()
                        .eq("user_id2_xq", userId)
                        .eq("user_id1_xq", userId2)
        ) > 0;
    }


    /**
     * 交换UserId的大小
     *
     * @param userId1
     * @param userId2
     */
    private static void swapUserId(Integer userId1, Integer userId2) {
        Integer temp = userId1;
        userId1 = userId2;
        userId2 = temp;
    }
}
